#include <hardware/emotiv/sbs2emotivmounter.h>

Sbs2EmotivMounter* Sbs2EmotivMounter::m_pInstance = 0;

/* Handle to hid_device, used in OSX for querying serial number and reading the data. */
#ifdef Q_OS_MAC
hid_device* Sbs2EmotivMounter::handle = 0;
#endif

Sbs2EmotivMounter* Sbs2EmotivMounter::New(QObject *parent)
{
    if (!m_pInstance)
        m_pInstance = new Sbs2EmotivMounter(parent);
    return m_pInstance;
}

Sbs2EmotivMounter::Sbs2EmotivMounter(QObject *parent)
{
    init();
}

Sbs2EmotivMounter::~Sbs2EmotivMounter()
{
    umount();
}

void Sbs2EmotivMounter::start()
{
    mount();
    readHardwareParameters();
}

void Sbs2EmotivMounter::stop()
{
    umount();
}

void Sbs2EmotivMounter::invalidate()
{
    /* Simply set first character to X which is not sane. */
    serialNumber[0] = 'X';
}

void Sbs2EmotivMounter::init()
{
    serialNumber = new char[16];
}

void Sbs2EmotivMounter::mount()
{
    mountedHardware = "emotiv";

#if defined(Q_OS_ANDROID)
    /* On Android we set the permissions on the device to 777 so our application doesn't have to be run as root. This is not the niceset way, but for now there doesn't seem to be any better one. */
    system("su -c 'chmod 777 /dev/hidraw1'");
#endif
    /* For OSX use hid_open to access the device. */
#ifdef Q_OS_MAC
    struct hid_device_info *devs, *cur_dev;
    int found = 0;
    wchar_t* _serial_number = 0;
    found = 0;
    devs = hid_enumerate(0x1234, 0xed02);
    cur_dev = devs;
    while (cur_dev) {
        if (found == 0)
        {
            _serial_number = (wchar_t*)malloc(60*sizeof(wchar_t));
            wcscpy(_serial_number,cur_dev->serial_number);
            found++;
        }
        else
        {
            if (wcscmp(cur_dev->serial_number,_serial_number) == 0)
            {
                //handle = hid_open_path(cur_dev->path);
                handle = hid_open(0x1234,0xed02,cur_dev->serial_number);
                found++;
                break;
            }
        }
        cur_dev = cur_dev->next;
    }

    if (!handle)
    {
        qDebug() << "unable to open device";
    }
#endif
}

void Sbs2EmotivMounter::umount()
{
    //nothing to do
}

QString Sbs2EmotivMounter::readSerialNumber()
{
    QString baseDirString = "/sys/bus/usb/devices/";
    QDir baseDir = QDir(baseDirString);
    QStringList topList = baseDir.entryList();
    QRegExp usbReg("\\d\\-\\d");
    QRegExp serialNumberReg("SN\\d{14}");
    QString serialNumber = "";

    QStringList usbList;
    foreach (QString name, topList)
    {
    if(name.contains("usb"))
    {
        usbList.append(name);
    }
    }

    foreach (QString name, usbList)
    {
    QString thisDirString = baseDirString+name;
    QDir thisDir = QDir(thisDirString);
    QStringList thisList = thisDir.entryList();

    foreach (QString name, thisList)
    {
        if (usbReg.exactMatch(name))
        {

        QString finalFileSerialString = thisDirString+QString("/")+name+QString("/serial");
        QFile file(finalFileSerialString);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            continue;

        QTextStream in(&file);

        QString line = in.readLine();

        if (serialNumberReg.exactMatch(line))
        {
            serialNumber = line;
            return serialNumber;
        }
        }
    }
    }
    return serialNumber;
}


void Sbs2EmotivMounter::readHardwareParameters()
{
    /* Temporary serial number. */
    char serialNumberNew[17];
    /* On OSX we use hid to query for the serial number. */
#ifdef Q_OS_MAC
    wchar_t wstr[17];

    if (!handle)
    {
        qDebug("unable to open device\n");
        return;
    }
    wstr[0] = 0x0000;
    /* Get serial number. */
    hid_get_serial_number_string(handle, wstr, sizeof(wstr));
    /* Copy serial number to our temporary container. */
    for (int i=0; i<16; ++i)
    {
        serialNumberNew[i] = (char)wstr[i];
    }
#elif  defined(Q_OS_ANDROID)
    QString s = readSerialNumber();
    if (s.compare("") == 0)
    {
        return;
    }
    for (int i= 0; i<16; ++i)
    {
        serialNumberNew[i] = s.at(i).toAscii();
    }
#else
    FILE *in;
    if (!(in = popen("lsusb -vd 1234:ed02 | grep iSerial | tail -c 17", "r")))
    {
        exit(1);
    }
    fgets(serialNumberNew, sizeof(serialNumberNew), in);
    pclose(in);
#endif
    /* New serial number is not sane. */
    if (!(serialNumberNew[0] == 'S' and serialNumberNew[1] == 'N'))
    {
        /* Old serial number was sane, which means we have lost the device.*/
        if (serialNumber[0] == 'S' and serialNumber[1] == 'N')
        {
            /* We no longer have real serial number. */
            invalidate();
            emit deviceLost();
        }
        /* Nothing else to do, new serial number is not sane. */
        return;
    }

    /* New serial number is  sane, is it different from the old one? */
    int diff = 0;
    for (int i= 0; i<16; ++i)
    {
        if (!(serialNumber[i] == serialNumberNew[i]))
            diff = 1;

    }

    /* Old and new serial numbers are the same, nothing to do. */
    if (!diff)
        return;
    /* New serial number is different than the old one, we hav a new device. */
    for (int i= 0; i<16; ++i)
    {
        /* Set current serial number to the new one. */
        serialNumber[i] = serialNumberNew[i];
    }

    identifier = QString(serialNumber);
    QMap<QString, QVariant> params = QMap<QString, QVariant>();
    params["serialNumber"] = QString(serialNumber);
    emit deviceFound(params);
}
